package com.heima.admin.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.heima.admin.mapper.ChannelMapper;
import com.heima.admin.mapper.SensitiveMapper;
import com.heima.admin.service.WemediaNewsAutoScanService;
import com.heima.aliyunsecurity.GreenImageScan;
import com.heima.aliyunsecurity.GreenTextScan;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.constants.wemedia.WemediaConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.article.ArticleFeign;
import com.heima.feigns.wemedia.WemediaFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @ClassName WemediaNewsAutoScanServiceImpl
 * @Description TODO
 * @Author AWei
 * @DATE 2021/4/17 9:49
 * @Version 1.0
 */
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {

    @Resource
    private WemediaFeign wemediaFeign;

    @Value("${file.oss.web-site}")
    private String webSite;

    @Resource
    private SensitiveMapper sensitiveMapper;

    @Resource
    private GreenTextScan greenTextScan;

    @Resource
    private GreenImageScan greenImageScan;

    @Resource
    private ChannelMapper channelMapper;

    @Resource
    private ArticleFeign articleFeign;

    @Resource
    private KafkaTemplate kafkaTemplate;

    @Override
    @GlobalTransactional(timeoutMills = 60000,rollbackFor = Exception.class)
    public void autoScanByMediaNewsId(Integer wmNewsId) {
        //1. 校验文章id是否为空
        if (ObjectUtils.isEmpty(wmNewsId)) {
            log.error("当前的审核id空");
            CustomException.custom(AppHttpCodeEnum.PARAM_INVALID, "文章id为null");
        }
        //2. 根据文章id查找文章信息
        WmNews wmNews = wemediaFeign.findById(wmNewsId);
        if (ObjectUtils.isEmpty(wmNews)) {
            log.error("审核的自媒体文章不存在，自媒体的id:{}", wmNewsId);
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "该id对应文章信息不存在");
        }
        //3. 检查文章状态是否为4或8；是则发布文章
        if ((WemediaConstants.WM_NEWS_HUMAN_PASS.equals(wmNews.getStatus())
                || WemediaConstants.WM_NEWS_AUTHED_STATUS.equals(wmNews.getStatus()))
                && wmNews.getPublishTime().before(new Date())) {
            publishArticle(wmNews);
            return;
        }
        //4. 若文章状态为1（待审核）开始审核
        if (WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(wmNews.getStatus())) {
            //抽取文章内容中的 文本和图片地址
            Map<String, Object> imagesAndText = extractImagesAndText(wmNews);
            //4.1 自管理敏感词审核
            boolean isSensitiveScan = auditSensitive((String) imagesAndText.get("text"), wmNews);
            if (!isSensitiveScan) {
                return;
            }

            //4.2 阿里云审核文本内容
            boolean isTextScan = auditTextScan((String) imagesAndText.get("text"), wmNews);
            if (!isTextScan) {
                return;
            }

            //4.3 阿里云图片审核
            boolean isImageScan = auditImagesScan((List<String>) imagesAndText.get("images"), wmNews);
            if (!isImageScan) {
                return;
            }

            //4.4 判断文章发布时间大于当前时间，修改状态8
            if (wmNews.getPublishTime().getTime() > System.currentTimeMillis()) {
                updateWmNews(wmNews, WemediaConstants.WM_NEWS_AUTHED_STATUS, "文章审核通过，待发布");
                return;
            }
            //5 保存文章相关数据
            publishArticle(wmNews);
            //TODO 6 同步索引库
        }
    }

    private boolean auditImagesScan(List<String> images, WmNews wmNews) {
        // 若无图则直接结束方法
        if (images.isEmpty()) {
            return true;
        }
        boolean flag = true;
        try {
            Map map = greenImageScan.imageUrlScan(images);

            //审核不通过
            if ("block".equals(map.get("suggestion"))) {
                updateWmNews(wmNews, (short) 2, "文章图片违规");
                flag = false;
            }
            //人工审核
            if ("review".equals(map.get("suggestion"))) {
                updateWmNews(wmNews, (short) 3, "文章图片疑似违规");
                flag = false;
            }

        } catch (Exception e) {
            e.printStackTrace();
            updateWmNews(wmNews, (short) 3, "阿里云审核失败，转入人工审核");
            flag = false;
        }

        return flag;
    }

    /**
     * @param content 文本内容
     * @param wmNews  文章信息对象
     * @return 审核结果
     * @Description 审核文本内容-阿里云内容安全
     */
    private boolean auditTextScan(String content, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenTextScan.greenTextScan(content);
            //审核不通过
            if ("block".equals(map.get("suggestion"))) {
                flag = false;
                updateWmNews(wmNews, (short) 2, "文本中内容有敏感词汇");
            }
            //需要人工审核
            if ("review".equals(map.get("suggestion"))) {
                flag = false;
                updateWmNews(wmNews, (short) 3, "文本中内容需要人工审核");
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文章阿里云内容审核失败，转入人工审核, e：{}", e);
            flag = false;
            updateWmNews(wmNews, (short) 3, "文章阿里云内容审核失败,转入需要人工审核");
        }
        return flag;
    }

    /**
     * @param content 文本内容
     * @param wmNews  文章信息对象
     * @return 审核结果
     * @Description 自定义敏感词审核
     */
    private boolean auditSensitive(String content, WmNews wmNews) {
        boolean flag = true;
        // 查询所有敏感词
        List<String> sensitiveList = sensitiveMapper.findAllSensitive();
        // 初始化 DFA搜索
        SensitiveWordUtil.initMap(sensitiveList);
        // 匹配敏感词
        Map<String, Integer> map = SensitiveWordUtil.matchWords(content);

        if (map.size() > 0) {
            flag = false;
            updateWmNews(wmNews, (short) 2, "文章中包含了敏感词:" + map);
        }

        return flag;
    }

    /**
     * @param wmNews 文章信息对象
     * @param status 文章状态
     * @param msg    审核失败原因
     * @Description 修改文章审核结果信息
     */
    private void updateWmNews(WmNews wmNews, short status, String msg) {
        wmNews.setStatus(status);
        wmNews.setReason(msg);
        ResponseResult responseResult = wemediaFeign.updateWmNews(wmNews);
        if (!responseResult.getCode().equals(0)) {
            CustomException.custom(AppHttpCodeEnum.UPDATE_ERROR, "文章修改失败");
        }
    }

    /**
     * @param wmNews 文章信息对象
     * @return 抽取的图片地址和文本内容封装成map
     * @Description 抽取文章内容中的图片地址和文本内容方法
     */
    private Map<String, Object> extractImagesAndText(WmNews wmNews) {
        Map<String, Object> map = new HashMap<>();
        //获取文章内容Content属性
        String content = wmNews.getContent();
        if (StringUtils.isBlank(content)) {
            log.error("文章内容为空 => {}" + content);
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "文章内容为空");
        }
        List<Map> contentList = JSON.parseArray(content, Map.class);
        //2. 抽取文本内容
        String text = contentList.stream().filter(k -> "text".equals(k.get("type")))
                .map(v -> v.get("value").toString())
                .collect(Collectors.joining("_hmtt_"));
        //标题也加入审核
        text = wmNews.getTitle() + "_hmtt_" + text;
        if (text.length() > 10000) {
            log.error("文章文本超过10000 => {}" + content);
            CustomException.custom(AppHttpCodeEnum.DATA_NOT_EXIST, "文章文本超过10000");
        }
        map.put("text", text);
        //抽取内容图片地址
        List<String> images = contentList.stream()
                .filter(k -> "image".equals(k.get("type")))
                .map(y -> y.get("value").toString())
                .distinct()
                .collect(Collectors.toList());
        //4.收集封面前缀
        String imageCover = wmNews.getImages();
        if (StringUtils.isNotBlank(imageCover)) {
            // 添加到内容图片列表
            Stream.of(imageCover.split(","))
                    .map(x -> webSite + x)
                    .distinct()
                    .forEach(images::add);
        }
        //图片地址去重
        images = images.stream().distinct().collect(Collectors.toList());
        //5. 封装图片地址
        map.put("images", images);
        return map;
    }

    /**
     * @param wmNews 文章信息对象
     * @Description 保存文章方法
     */
    private void publishArticle(WmNews wmNews) {
        //保存app相关文章
        ResponseResult responseResult = saveArticle(wmNews);
        if (!responseResult.getCode().equals(0)) {
            throw new RuntimeException("文章审核保存app文章失败");
        }
        //修改自媒体文章状态为9,审核通过
        wmNews.setArticleId((Long) responseResult.getData());
        updateWmNews(wmNews, WemediaConstants.WM_NEWS_PUBLISH_STATUS, "文章审核通过");
        //创建索引（为后续app端的搜索功能做数据准备）
        // ==============新加代码==============================================
        Map map = new HashMap();
        map.put("articleId",wmNews.getArticleId());
        kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD,JSON.toJSONString(map));
        // ==============新加代码==============================================
    }

    /**
     * @param wmNews 文章信息对象
     * @return 携带文章id的响应结果
     * @Description 保存文章方法（保存三张表：articleContent、articleConfig、article）
     */
    private ResponseResult saveArticle(WmNews wmNews) {
        //1 封装数据 dto
        ArticleDto articleDto = new ArticleDto();
        BeanUtils.copyProperties(wmNews, articleDto);
        articleDto.setId(null);
        if (wmNews.getArticleId() != null) {
            articleDto.setId(wmNews.getArticleId());
        }
        // 设置属性
        articleDto.setLayout(wmNews.getType());
        articleDto.setFlag((byte) 0);

        //2 设置频道
        if (wmNews.getChannelId() != null) {
            articleDto.setChannelId(wmNews.getChannelId());
            AdChannel channel = channelMapper.selectById(wmNews.getChannelId());
            if (!ObjectUtils.isEmpty(channel)) {
                articleDto.setChannelName(channel.getName());
            }
        }

        //3 设置作者信息
        // 查wm_user 状态
        WmUser wmUser = wemediaFeign.findWmUserById(wmNews.getUserId());
        // 用户不存在或状态被封禁
        if (ObjectUtils.isEmpty(wmUser) || wmUser.getStatus() != 9) {
            CustomException.custom(AppHttpCodeEnum.DATA_ERROR, "用户数据错误");
        }
        if (StringUtils.isNotBlank(wmUser.getName())) {
            // 文章作者和自媒体名称一致
            articleDto.setAuthorName(wmUser.getName());
        }

        //4 设置文章内容
        articleDto.setContent(wmNews.getContent());

        // 远程调用保存三张表数据, 返回对象必须携带文章id
        return articleFeign.saveArticle(articleDto);
    }
}
